﻿// ==UserScript==
// @name           openNewTabLite.uc.js
// @namespace      ithinc#mozine.cn
// @description    Open URL/Search/Bookmark/History/Homepage in New Tab
// @include        main
// @compatibility  Firefox 3.0.x 3.5.x
// @author         ithinc
// @homepage       http://board.mozest.com/thread-29606-1-1
// @version        20091215.0.0.43 标签页右键菜单增加最近关闭标签页列表（不再移动历史菜单上的最近关闭标签页列表）
// @version        20091215.0.0.42 撤销关闭标签页按钮增加下拉菜单
// @version        20091210.0.0.41 部分功能增加快捷键
// @version        20091210.0.0.40 Ctrl+Tab切换的两种模式
// @version        20091209.0.0.39 中文菜单
// @version        20091209.0.0.38 单窗口模式下隐藏相关菜单项
// @version        20091209.0.0.37 自动创建about:config配置项
// @version        20091209.0.0.36 改进撤销关闭标签页按钮的安装位置
// @version        20091208.0.0.35 单窗口模式
// @version        20091208.0.0.34 在新标签打开链接时继承历史
// @version        20091208.0.0.33 删除所有链接在当前标签打开的选项（设置browser.link.open_newwindow=1即可）
// @version        20091206.0.0.32 增加所有链接在当前标签打开的选项
// @version        20091206.0.0.31 Ctrl+Tab切换到上次浏览的标签
// @version        20091206.0.0.30 修复图标化标签页导致其他标签页关闭按钮显示不正常的BUG
// @version        20091205.0.0.29 增加隐藏标签页功能
// @version        20091205.0.0.28 增加图标化标签页功能
// @version        20091205.0.0.27 增加保护标签页、锁定标签页功能
// @version        20091204.0.0.26 导航栏增加撤销关闭标签页按钮
// @version        20091204.0.0.25 改进未读标签的标记
// @version        20091203.0.0.24 标签页右键菜单增加复制标签页
// @version        20091201.0.0.23 地址栏回车键新标签根据userChromeJS.openNewTabLite.loadUrlInBackground在后台打开
// @version        20091201.0.0.22 修复3.6b5pre下地址栏回车键新标签无法后台打开的BUG
// @version        20091201.0.0.21 修复3.6b5pre下空白标签页时不能打开单个url的BUG
// @version        20091201.0.0.20 修复3.5.5下关闭最后一个标签的BUG
// @version        20091130.0.0.19 文件菜单打开文件命令在新标签页打开
// @version        20091130.0.0.18 地址栏回车键新标签根据browser.tabs.loadInBackground在后台打开
// @version        20091129.0.0.17 修复3.1b3/3.5b4pre下关闭最后一个标签的BUG
// @version        20090503.0.0.16 openUILinkIn函数增加tabhidden与sidebar两个选项
// @version        20090503.0.0.15 Tagsifter侧边栏在新标签打开
// @version        20090426.0.0.14 增加所有链接在新标签打开的选项
// @version        20090426.0.0.13 关闭标签页时选择下一个未读标签
// @version        20090426.0.0.12 连续打开后台标签时保持原有顺序
// @version        20090423.0.0.11 在当前标签页的右侧打开新标签页
// @version        20090423.0.0.10 关闭标签页时激活上次浏览的标签功能兼容Fx 3.1b3/3.5b4pre
// @version        20090422.0.0.9 关闭标签页时激活上次浏览的标签
// @version        20090422.0.0.8 总在当前标签页打开Bookmarklet
// @version        20090421.0.0.7 允许利用已有访问历史的空白标签页
// @version        20090421.0.0.6 隐藏历史菜单上可能多余的分隔线
// @version        20090421.0.0.5 undoCloseTab/duplicateTab不使用已有空白标签页
// @version        20090421.0.0.4 新建标签页时利用已有空白标签页、自动关闭多余的新建空白标签页
// @version        20090420.0.0.3 书签、历史侧边栏在新标签打开
// @version        20090418.0.0.2 修复Location Bar2在新标签打开的问题
// @version        20090418.0.0.1 Initial release
// @note           browser.search.openintab: 搜索栏回车键在新标签打开，缺省值为false
// @note           browser.tabs.loadBookmarksInBackground: 书签/历史等新标签左键在后台打开/中键在前台打开，缺省值为false
// @note           browser.link.open_newwindow: 新窗口链接的打开位置，1=当前标签，2=新窗口，3=新标签（默认值）
// @note           browser.link.open_newwindow.restriction: 0=divert all windows, 1=divert no windows, 2=divert all windows except popups（默认值）
// @note           browser.ctrlTab.mostRecentlyUsed: Ctrl+Tab按最近访问的顺序循环或在最近两个标签页之间切换，缺省值为false
// @note           userChromeJS.openNewTabLite.loadUrlInBackground:地址栏回车键新标签在后台打开，缺省值为false
// @note           userChromeJS.openNewTabLite.loadInBlank.current: 自动利用当前空白标签页，缺省值为true
// @note           userChromeJS.openNewTabLite.loadInBlank.last: 自动利用尾部空白标签页，缺省值为true
// @note           userChromeJS.openNewTabLite.loadInBlank.all: 自动利用所有空白标签页，缺省值为false
// @note           userChromeJS.openNewTabLite.openTabNext: 在当前标签页的右侧打开新标签页，缺省值为true
// @note           userChromeJS.openNewTabLite.openTabNextInverse: 连续打开后台标签时保持原有顺序，缺省值为true
// @note           userChromeJS.openNewTabLite.selectLastFocusedOnClose: 关闭标签页时激活上次浏览的标签，缺省值为true
// @note           userChromeJS.openNewTabLite.selectNextUnreadOnClose: 关闭标签页时选择下一个未读标签，缺省值为true
// @note           userChromeJS.openNewTabLite.openAllLinksInTab: 所有链接在新标签打开，缺省值为false
// @note           userChromeJS.openNewTabLite.openLinkWithHistory: 在新标签打开链接时继承历史，缺省值为false
// @note           userChromeJS.openNewTabLite.singleWindowMode: 单窗口模式，缺省值为false
// ==/UserScript==

/* :::: Open URL/Search/Bookmark/History/Homepage in New Tab :::: */

var gTabEventListeners = {
  init: function() {
    gBrowser.onTabOpen = function onTabOpen(event) {};
    gBrowser.onTabMove = function onTabMove(event) {};
    gBrowser.onTabClose = function onTabClose(event) {};
    gBrowser.onTabSelect = function onTabSelect(event) {};
    gBrowser.onTabRestoring = function onTabRestoring(event) {};
    gBrowser.onTabRestored = function onTabRestored(event) {};
    gBrowser.mTabContainer.addEventListener("TabOpen", this, false);
    gBrowser.mTabContainer.addEventListener("TabMove", this, false);
    gBrowser.mTabContainer.addEventListener("TabClose", this, false);
    gBrowser.mTabContainer.addEventListener("TabSelect", this, false);
    gBrowser.mTabContainer.addEventListener("SSTabRestoring", this, false);
    gBrowser.mTabContainer.addEventListener("SSTabRestored", this, false);
    window.addEventListener("unload", this, false);
  },

  uninit: function() {
    gBrowser.mTabContainer.removeEventListener("TabOpen", this, false);
    gBrowser.mTabContainer.removeEventListener("TabMove", this, false);
    gBrowser.mTabContainer.removeEventListener("TabClose", this, false);
    gBrowser.mTabContainer.removeEventListener("TabSelect", this, false);
    gBrowser.mTabContainer.removeEventListener("SSTabRestoring", this, false);
    gBrowser.mTabContainer.removeEventListener("SSTabRestored", this, false);
    window.removeEventListener("unload", this, false);
  },

  handleEvent: function(event) {
    switch (event.type) {
      case "TabOpen": gBrowser.onTabOpen(event);break;
      case "TabMove": gBrowser.onTabMove(event);break;
      case "TabClose": gBrowser.onTabClose(event);break;
      case "TabSelect": gBrowser.onTabSelect(event);break;
      case "SSTabRestoring": gBrowser.onTabRestoring(event);break;
      case "SSTabRestored": gBrowser.onTabRestored(event);break;
      case "unload": this.uninit();break;
    }
  }
};
gTabEventListeners.init();

//在新标签打开书签、历史、URL、搜索
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  //地址栏、搜索栏、书签菜单、书签工具栏、历史菜单、主页按钮
  //左键在新标签页打开
  //中键在新标签页后台打开
  //Ctrl+左键在当前标签页打开
  hookCode("whereToOpenLink", /if \(ctrl(.|\n)*(?=else if \(alt)/, function() {
    if (middle && middleUsesTabs) {
      return "tabshifted";
    }
    else if (ctrl) {
      return "tab";
    }
  });

  hookCode("whereToOpenLink", /return "tab";|return "current";/g, function(s) {
    var s1 = (function() {
      var node = e && e.originalTarget;
      while (node) {
        switch (node.id) {
          case "identity-box":
          case "go-button":
          case "PopupAutoCompleteRichResult": //地址栏下拉菜单
          case "searchbar":
          case "PopupAutoComplete": //搜索栏下拉菜单
          case "bookmarksMenu":
          case "bookmarksBarContent":
          case "history-menu":
          case "home-button":
            return "current";
        }
        node = node.parentNode;
      }
      return "tab";
    }).toString().replace(/^function.*{|}$/g, "");

    var s2 = (function() {
      var node = e && e.originalTarget;
      while (node) {
        switch (node.id) {
          case "identity-box":
          case "go-button":
          case "PopupAutoCompleteRichResult": //地址栏下拉菜单
          case "searchbar":
          case "PopupAutoComplete": //搜索栏下拉菜单
          case "bookmarksMenu":
          case "bookmarksBarContent":
          case "history-menu":
          case "home-button":
            return "tab";
        }
        node = node.parentNode;
      }
      return "current";
    }).toString().replace(/^function.*{|}$/g, "");

    return s == 'return "tab";' ? s1 : s2;
  });

  //总在当前标签页打开Bookmarklet
  hookCode("openUILinkIn", /(?=if \(where == "save"\))/, function() {
    if (url.match(/^javascript:/))
      where = "current";
  });

  //书签、历史、Tagsifter侧边栏
  document.getElementById("sidebar-box").addEventListener("load", function(event) {
    var document = event.target;
    if (document.location == "chrome://browser/content/bookmarks/bookmarksPanel.xul"
        || document.location == "chrome://browser/content/history/history-panel.xul"
        || document.location == "chrome://bookmarktags/content/tagBrowserSidebar.xul") {
      hookCode("whereToOpenLink", /if \(ctrl(.|\n)*(?=else if \(alt)/, function() {
        if (middle && middleUsesTabs) {
          return "tabshifted";
        }
        else if (ctrl) {
          return "tab";
        }
      }, document.defaultView);

      hookCode("whereToOpenLink", /return "tab";|return "current";/g, function(s) {
        return s == 'return "tab";' ? 'return "current";' : 'return "tab";';
      }, document.defaultView);

      hookCode("openUILinkIn", /(?=if \(where == "save"\))/, function() {
        if (url.match(/^javascript:/))
          where = "current";
      }, document.defaultView);
    }
  }, true);

  //地址栏回车键在新标签页打开，Alt+回车键在当前标签页打开
  if ("BrowserLoadURL" in window) { //Firefox 3.0.x
    hookCode("BrowserLoadURL", "openUILink", "handleURLBarRevert();content.focus();$&");
    hookCode("BrowserLoadURL", "aTriggeringEvent && aTriggeringEvent.altKey", "!($&)");
    hookCode("BrowserLoadURL", "aTriggeringEvent.preventDefault();", "");
    hookCode("BrowserLoadURL", "aTriggeringEvent.stopPropagation();", "");
    hookCode("BrowserLoadURL", /.*loadOneTab.*/, function(s) {
      return s.replace("false", "getPref('userChromeJS.openNewTabLite.loadUrlInBackground', false)");
    });
  }
  else { //Firefox 3.5b4pre
    hookCode("gURLBar.handleCommand", "openUILink", "this.handleRevert();content.focus();$&");
    hookCode("gURLBar.handleCommand", "aTriggeringEvent && aTriggeringEvent.altKey", "!($&)");
    hookCode("gURLBar.handleCommand", "aTriggeringEvent.preventDefault();", "");
    hookCode("gURLBar.handleCommand", "aTriggeringEvent.stopPropagation();", "");
    hookCode("gURLBar.handleCommand", /.*loadOneTab.*/, function(s) {
      return s.replace("false", "getPref('userChromeJS.openNewTabLite.loadUrlInBackground', false)");
    });
  }

  //文件菜单打开文件命令在新标签页打开
  hookCode("openTopWin", "openUILink(url, {});", "openUILinkIn(url, 'tab');");

  // openUILinkIn函数增加tabforeground/tabbackground/tabhidden/sidebar等选项
  hookCode("openUILinkIn", /(?=case "tab")/, "case 'tabforeground':");
  hookCode("openUILinkIn", /(?=case "tab")/, "case 'tabbackground':");
  hookCode("openUILinkIn", /.*loadOneTab.*/, function(s) {
    return s.replace("loadInBackground", "where=='tabforeground' ? false : where=='tabbackground' ? true : loadInBackground");
  });
  hookCode("openUILinkIn", /(?=default)/, "case 'tabhidden':");
  hookCode("openUILinkIn", /(?=default)/, function() {
    var b = w.getBrowser();
    var found = false;
    for(var i=0; i<b.mTabs.length; i++) {
      if (b.getBrowserForTab(b.mTabs[i]).currentURI.spec == url) {
        b.selectedTab = b.mTabs[i];
        found = true;
        break;
      }
    }
    if (!found) {
      b.loadOneTab(url, referrerUrl, null, postData, false, allowThirdPartyFixup || false);
    }
    b.selectedTab.setAttribute("hidden", true);
  });
  hookCode("openUILinkIn", /(?=default)/, "break;");
  hookCode("openUILinkIn", /(?=default)/, "case 'sidebar': openWebPanel('', url);break;");

  //双击关闭标签页
  hookCode("gBrowser.onTabBarDblClick", "{", function() {
    if (!this._blockDblClick && aEvent.button == 0 && aEvent.target.localName == "tab") {
      this.removeTab(aEvent.target);
      aEvent.stopPropagation();
      return;
    }
  });

  //中键恢复上一个关闭的标签页
  hookCode("gBrowser.onTabClick", "{", function() {
    if (event.button == 1 && event.target.localName == "tabs") {
      undoCloseTab();
      event.stopPropagation();
      return;
    }
  });

  //标签页右键菜单增加最近关闭标签页列表
  var tabContextMenu = document.getAnonymousElementByAttribute(gBrowser, "anonid", "tabContextMenu");
  var closedTabsMenu = tabContextMenu.insertBefore(document.createElement('menu'), document.getElementById("context_undoCloseTab").nextSibling);
  closedTabsMenu.setAttribute("id", "context_recentlyClosedTabs");
  closedTabsMenu.setAttribute("label", "Recently Closed Tabs");
  closedTabsMenu.appendChild(document.createElement("menupopup")).setAttribute("onpopupshowing", "this.parentNode.populateUndoSubmenu();");
  eval("closedTabsMenu.populateUndoSubmenu = "  + HistoryMenu.populateUndoSubmenu.toString()
    .replace(/this/g, 'HistoryMenu')
    .replace('document.getElementById("historyUndoPopup")', 'this.firstChild')
  );
  hookCode("gBrowser.updatePopupMenu", "}", function() {
    document.getElementById('context_recentlyClosedTabs').hidden = document.getElementById('context_undoCloseTab').hidden;
  });

  //导航栏增加撤销关闭标签页按钮
  var refNode = document.getElementById('home-button') || document.getElementById('stop-button') || document.getElementById('reload-button');
  if (refNode) {
    var undoCloseTabButton = refNode.parentNode.insertBefore(document.createElement("toolbarbutton"), refNode.nextSibling);
    undoCloseTabButton.setAttribute("id", "undoclosetab-button");
    undoCloseTabButton.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
    undoCloseTabButton.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Undo Close Tab" : "\u64A4\u9500\u5173\u95ED\u6807\u7B7E\u9875");
    undoCloseTabButton.setAttribute("image", "");
    undoCloseTabButton.setAttribute("tooltiptext", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Undo Close Tab" : "\u64A4\u9500\u5173\u95ED\u6807\u7B7E\u9875");
    undoCloseTabButton.setAttribute("command", "History:UndoCloseTab");
    undoCloseTabButton.setAttribute("type", "menu-button");
    undoCloseTabButton.appendChild(document.createElement("menupopup")).setAttribute("onpopupshowing", "this.parentNode.populateUndoSubmenu();");
    eval("undoCloseTabButton.populateUndoSubmenu = " + HistoryMenu.populateUndoSubmenu.toString()
      .replace(/this/g, 'HistoryMenu')
      .replace('document.getElementById("historyUndoPopup")', 'this.firstChild')
    );

    var ss = document.styleSheets[document.styleSheets.length-1];
    ss.insertRule('#undoclosetab-button[disabled="true"] {opacity: 0.5 !important;}', ss.cssRules.length);
    hookCode("gBrowser.onTabClose", "}", function() {
      document.getElementById("undoclosetab-button").disabled = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore).getClosedTabCount(window) == 0;
    });
    hookCode("gBrowser.onTabRestoring", "}", function() {
      document.getElementById("undoclosetab-button").disabled = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore).getClosedTabCount(window) == 0;
    });
  }
})();

//新建标签页时利用已有空白标签页
//自动关闭多余的新建空白标签页
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  hookCode("gBrowser.addTab", /(?=this._browsers)/, function() {
    if (["sss_restoreWindow", "sss_undoCloseTab", "sss_duplicateTab"].indexOf(arguments.callee.caller.name) == -1) {
      var t = this.getBlankTab();
      if (t) {
        var b = this.getBrowserForTab(t);
        b.userTypedValue = aURI;

        var flags = aAllowThirdPartyFixup ? Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
        try {
          b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
        } catch (ex) {
        }

        return t;
      }
    }
  });

  gBrowser.getBlankTab = function() {
    return getPref("userChromeJS.openNewTabLite.loadInBlank.current", true) && this.isBlankTab(this.mCurrentTab) && this.mCurrentTab
        || getPref("userChromeJS.openNewTabLite.loadInBlank.last", true) && this.isBlankTab(this.mTabContainer.lastChild) && this.mTabContainer.lastChild
        || getPref("userChromeJS.openNewTabLite.loadInBlank.all", false) && this.getFirstBlankTab()
        || null;
  }

  gBrowser.getFirstBlankTab = function(aTab) {
    for (var i=0; i<this.mTabs.length; i++) {
      if (this.isBlankTab(this.mTabs[i]) && this.mTabs[i] != aTab) {
        return this.mTabs[i];
      }
    }
    return null;
  }

  hookCode("gBrowser.removeTab", "{", "aTab.setAttribute('removing', true);");
  gBrowser.isBlankTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    return this.getBrowserForTab(aTab).currentURI.spec == "about:blank"
          && !aTab.hasAttribute("busy")
          && !aTab.hasAttribute("removing")
  }

  hookCode("gBrowser.onTabOpen", "}", function() {
    this.getBrowserForTab(event.target).addEventListener("load", function() {
      this.removeEventListener("load", arguments.callee, true);

      if (this.currentURI.spec == "about:blank" && this.sessionHistory.count < 2) {
        var tab = document.getAnonymousElementByAttribute(gBrowser, "linkedpanel", this.parentNode.id);
        var firstBlankTab = gBrowser.getFirstBlankTab(tab);
        if (firstBlankTab) {
          gBrowser.selectedTab = firstBlankTab;
          gURLBar.focus();

          setTimeout(function(aTab) {
            gBrowser.removeTab(aTab);
          }, 0, tab);
        }
      }
    }, true);
  });
})();

//在当前标签页的右侧打开新标签页
//连续打开后台标签时保持原有顺序
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  gBrowser.mTabContainer._tabOffset = 1;
  hookCode("gBrowser.onTabOpen", "}", function() {
    if (getPref("userChromeJS.openNewTabLite.openTabNext", true)) {
      this.moveTabTo(event.target, this.mCurrentTab._tPos + this.mTabContainer._tabOffset);
    }
    if (getPref("userChromeJS.openNewTabLite.openTabNextInverse", true)) {
      this.mTabContainer._tabOffset++;
    }
  });

  hookCode("gBrowser.onTabClose", "}", function() {
    if (this.mCurrentTab._tPos < event.target._tPos
        && event.target._tPos < this.mCurrentTab._tPos + this.mTabContainer._tabOffset) {
      this.mTabContainer._tabOffset--;
    }
  });

  hookCode("gBrowser.onTabSelect", "}", function() {
    this.mTabContainer._tabOffset = 1;
  });

  hookCode("gBrowser.moveTabTo", /(?=return aTab;)/, function() {
    if (arguments.callee.caller.name != "onTabOpen") {
      this.mTabContainer._tabOffset = 1;
    }
  });
})();

//关闭标签页时激活上次浏览的标签
//Ctrl+TAB切换到上次浏览的标签
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  gBrowser.mTabContainer._tabHistory = [];
  hookCode("gBrowser.onTabSelect", "}", function() {
    var index = this.mTabContainer._tabHistory.indexOf(event.target);
    if (index > -1)
      this.mTabContainer._tabHistory.splice(index, 1);
    this.mTabContainer._tabHistory.push(event.target);
  });
  hookCode("gBrowser.onTabClose", "}", function() {
    var index = this.mTabContainer._tabHistory.indexOf(event.target);
    if (index > -1)
      this.mTabContainer._tabHistory.splice(index, 1);
  });

  gBrowser.getLastSelectedTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    for (var i = this.mTabContainer._tabHistory.length-1;i>=0;i--) {
      var tab = this.mTabContainer._tabHistory[i];
      if (tab != aTab)
        return tab;
    }
    return null;
  }

  if ("_blurTab" in gBrowser) { //Firefox 3.5b4pre
    hookCode("gBrowser._blurTab", /this.selectedTab =/g, "$& getPref('userChromeJS.openNewTabLite.selectLastFocusedOnClose', true) && this.getLastSelectedTab(aTab) ||");
  }
  else if ("_endRemoveTab" in gBrowser) { //Firefox 3.1b3
    hookCode("gBrowser._endRemoveTab", /this.selectedTab =/g, "$& getPref('userChromeJS.openNewTabLite.selectLastFocusedOnClose', true) && this.getLastSelectedTab(aTab) ||");
  }
  else { //Firefox 3.0.x
    hookCode("gBrowser.removeTab", /this.selectedTab =/g, "$& getPref('userChromeJS.openNewTabLite.selectLastFocusedOnClose', true) && this.getLastSelectedTab(aTab) ||");
  }

  window.addEventListener("keypress", function(event) {
    if (event.ctrlKey && !event.altKey && !event.metaKey && event.keyCode==event.DOM_VK_TAB) {
      if (gBrowser.mTabContainer._tabHistory.length < 2)
        return;

      if (getPref("browser.ctrlTab.mostRecentlyUsed", false)) {
        if (!event.shiftKey) {
          gBrowser.mTabContainer._tabHistory.unshift(gBrowser.mTabContainer._tabHistory.pop());
          gBrowser.selectedTab = gBrowser.mTabContainer._tabHistory.pop();
        }
        else {
          gBrowser.selectedTab = gBrowser.mTabContainer._tabHistory.shift();
        }
      }
      else {
        gBrowser.selectedTab = gBrowser.getLastSelectedTab();
      }
      event.stopPropagation();
      event.preventDefault();
    }
  }, true);
})();

//标记未读标签
//关闭标签页时选择下一个未读标签
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  hookCode("gBrowser.mTabProgressListener", 'var location = aRequest.QueryInterface(nsIChannel).URI;', 'if (this.mTab.getAttribute("selected")!="true") this.mTab.setAttribute("unread", true); $&');
  for (var i=0; i<gBrowser.mTabs.length; i++) {
    hookCode("gBrowser.mTabListeners[" + i + "].onStateChange", 'var location = aRequest.QueryInterface(nsIChannel).URI;', 'if (this.mTab.getAttribute("selected") != "true") this.mTab.setAttribute("unread", true); $&');
    if(gBrowser.mTabs[i].getAttribute("selected")!="true")
      gBrowser.mTabs[i].setAttribute("unread", true);
  }

  hookCode("gBrowser.onTabSelect", "}", function() {
    event.target.removeAttribute("unread");
  });

  hookCode("gBrowser.onTabOpen", "}", function() {
    event.target.setAttribute("opener", this.mCurrentTab.getAttribute("linkedpanel"));
  });

  gBrowser.getNextUnreadTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;
    else if (aTab != this.mCurrentTab)
      return this.mCurrentTab;

    if (aTab.nextSibling
        && aTab.nextSibling.getAttribute("unread") == "true"
        && (aTab.nextSibling.getAttribute("opener") == aTab.getAttribute("opener")
            || aTab.nextSibling.getAttribute("opener") == aTab.getAttribute("linkedpanel"))) {
      return aTab.nextSibling;
    }
    else if (aTab.previousSibling
        && aTab.previousSibling.getAttribute("unread") == "true"
        && aTab.previousSibling.getAttribute("opener") == aTab.getAttribute("opener")) {
      return aTab.previousSibling;
    }
    else {
      return null;
    }
  }

  if ("_blurTab" in gBrowser) { //Firefox 3.5b4pre
    hookCode("gBrowser._blurTab", /this.selectedTab =/g, "$& getPref('userChromeJS.openNewTabLite.selectNextUnreadOnClose', true) && this.getNextUnreadTab(aTab) ||");
  }
  else if ("_endRemoveTab" in gBrowser) { //Firefox 3.1b3
    hookCode("gBrowser._endRemoveTab", /(?=aTab._selected)/, "var nextUnreadTab = getPref('userChromeJS.openNewTabLite.selectNextUnreadOnClose', true) ? this.getNextUnreadTab(aTab) : null;");
    hookCode("gBrowser._endRemoveTab", /this.selectedTab =/g, "$& nextUnreadTab ||");
  }
  else { //Firefox 3.0.x
    hookCode("gBrowser.removeTab", /(?=oldTab._selected)/, "var nextUnreadTab = getPref('userChromeJS.openNewTabLite.selectNextUnreadOnClose', true) ? this.getNextUnreadTab(oldTab) : null;");
    hookCode("gBrowser.removeTab", /this.selectedTab =/g, "$& nextUnreadTab ||");
  }
})();

//复制标签页
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  var tabContextMenu = document.getAnonymousElementByAttribute(gBrowser, "anonid", "tabContextMenu");
  var duplicateTabMenuitem = tabContextMenu.insertBefore(document.createElement("menuitem"), document.getElementById("context_newTab").nextSibling);
  duplicateTabMenuitem.setAttribute("id", "context_duplicateTab");
  duplicateTabMenuitem.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Duplicate Tab" : "\u590D\u5236\u6807\u7B7E\u9875");
  duplicateTabMenuitem.setAttribute("accesskey", "D");
  duplicateTabMenuitem.setAttribute("key", "key_duplicateTab");
  duplicateTabMenuitem.setAttribute("oncommand", "var aTab=gBrowser.mContextTab;var bTab=gBrowser.duplicateTab(aTab);gBrowser.selectedTab=gBrowser.moveTabTo(bTab, bTab._tPos>aTab._tPos ? aTab._tPos+1 : aTab._tPos);");

  var duplicateTabKey = document.getElementById("mainKeyset").appendChild(document.createElement("key"));
  duplicateTabKey.setAttribute("id", "key_duplicateTab");
  duplicateTabKey.setAttribute("modifiers", "accel,alt");
  duplicateTabKey.setAttribute("key", "T");
  duplicateTabKey.setAttribute("oncommand", "var aTab=gBrowser.selectedTab;var bTab=gBrowser.duplicateTab(aTab);gBrowser.selectedTab=gBrowser.moveTabTo(bTab, bTab._tPos>aTab._tPos ? aTab._tPos+1 : aTab._tPos);");
})();

//保护标签页、锁定标签页、冻结标签页
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  var tabContextMenu = document.getAnonymousElementByAttribute(gBrowser, "anonid", "tabContextMenu");
  var protectTabMenuitem = tabContextMenu.insertBefore(document.createElement("menuitem"), document.getElementById("context_closeTab"));
  protectTabMenuitem.setAttribute("id", "context_protectTab");
  protectTabMenuitem.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Protect Tab" : "\u4FDD\u62A4\u6807\u7B7E\u9875");
  protectTabMenuitem.setAttribute("type", "checkbox");
  protectTabMenuitem.setAttribute("accesskey", "P");
  protectTabMenuitem.setAttribute("key", "key_protectTab");
  protectTabMenuitem.setAttribute("oncommand", "gBrowser.protectTab(gBrowser.mContextTab);");

  var lockTabMenuitem = tabContextMenu.insertBefore(document.createElement("menuitem"), document.getElementById("context_closeTab"));
  lockTabMenuitem.setAttribute("id", "context_lockTab");
  lockTabMenuitem.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Lock Tab" : "\u9501\u5B9A\u6807\u7B7E\u9875");
  lockTabMenuitem.setAttribute("type", "checkbox");
  lockTabMenuitem.setAttribute("accesskey", "L");
  lockTabMenuitem.setAttribute("key", "key_lockTab");
  lockTabMenuitem.setAttribute("oncommand", "gBrowser.lockTab(gBrowser.mContextTab);");

  var freezeTabMenuitem = tabContextMenu.insertBefore(document.createElement("menuitem"), document.getElementById("context_closeTab"));
  freezeTabMenuitem.setAttribute("id", "context_freezeTab");
  freezeTabMenuitem.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Freeze Tab" : "\u51BB\u7ED3\u6807\u7B7E\u9875");
  freezeTabMenuitem.setAttribute("type", "checkbox");
  freezeTabMenuitem.setAttribute("accesskey", "F");
  freezeTabMenuitem.setAttribute("key", "key_freezeTab");
  freezeTabMenuitem.setAttribute("oncommand", "gBrowser.freezeTab(gBrowser.mContextTab);");
  tabContextMenu.insertBefore(document.createElement("menuseparator"), document.getElementById("context_closeTab"));

  hookCode("gBrowser.updatePopupMenu", "}", function() {
    document.getElementById("context_protectTab").setAttribute("checked", this.mContextTab.hasAttribute("protected"));
    document.getElementById("context_lockTab").setAttribute("checked", this.mContextTab.hasAttribute("locked"));
    document.getElementById("context_freezeTab").setAttribute("checked", this.mContextTab.hasAttribute("protected") && this.mContextTab.hasAttribute("locked"));
    document.getElementById("context_closeTab").disabled = this.mContextTab.hasAttribute("protected");
  });

  hookCode("gBrowser.onTabRestoring", "}", function() {
    var aTab = event.target;
    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (ss.getTabValue(aTab, "protected")) {
      aTab.setAttribute("protected", true);
    }
    if (ss.getTabValue(aTab, "locked")) {
      aTab.setAttribute("locked", true);
    }
  });

  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
  for (var i=0; i<gBrowser.mTabs.length; i++) {
    var aTab = gBrowser.mTabs[i];
    if (ss.getTabValue(aTab, "protected")) {
      aTab.setAttribute("protected", true);
    }
    if (ss.getTabValue(aTab, "locked")) {
      aTab.setAttribute("locked", true);
    }
  }

  gBrowser.protectTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (aTab.hasAttribute("protected")) {
      aTab.removeAttribute("protected");ss.deleteTabValue(aTab, "protected");
    }
    else {
      aTab.setAttribute("protected", true);ss.setTabValue(aTab, "protected", true);
    }
  }

  gBrowser.lockTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (aTab.hasAttribute("locked")) {
      aTab.removeAttribute("locked");ss.deleteTabValue(aTab, "locked");
    }
    else {
      aTab.setAttribute("locked", true);ss.setTabValue(aTab, "locked", true);
    }
  }

  gBrowser.freezeTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (aTab.hasAttribute("protected") && aTab.hasAttribute("locked")) {
      aTab.removeAttribute("protected");ss.deleteTabValue(aTab, "protected");
      aTab.removeAttribute("locked");ss.deleteTabValue(aTab, "locked");
    }
    else {
      aTab.setAttribute("protected", true);ss.setTabValue(aTab, "protected", true);
      aTab.setAttribute("locked", true);ss.setTabValue(aTab, "locked", true);
    }
  }

  hookCode("gBrowser.duplicateTab", /.*ss.duplicateTab.*/, function() {
    var tab = ss.duplicateTab(window, aTab);
    if (tab.hasAttribute("protected")) {
      this.protectTab(tab);
    }
    if (tab.hasAttribute("locked")) {
      this.lockTab(tab);
    }
    return tab;
  });

  hookCode("gBrowser.removeTab", "{", function() {
    if (aTab.hasAttribute("protected")) {
      return;
    }
  });

  hookCode("gBrowser.loadURI", "{", function() {
    if (this.selectedTab.hasAttribute("locked")) {
      return this.loadOneTab(aURI, aReferrerURI, aCharset, null, getPref("browser.tabs.loadInBackground", false), false);
    }
  });

  hookCode("gBrowser.loadURIWithFlags", "{", function() {
    if (this.selectedTab.hasAttribute("locked")) {
      return this.loadOneTab(aURI, aReferrerURI, aCharset, aPostData, getPref("browser.tabs.loadInBackground", false), aFlags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
    }
  });

  hookCode("contentAreaClick", /(?=.*else.*\n.*handleLinkClick)/, function() {
    if (gBrowser.selectedTab.hasAttribute("locked")) {
      openNewTabWith(wrapper.href, wrapper.ownerDocument, null, event, false);
      event.stopPropagation();
      return false;
    }
  });
})();

//图标化标签页
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  var tabContextMenu = document.getAnonymousElementByAttribute(gBrowser, "anonid", "tabContextMenu");
  var faviconizeTabMenuitem = tabContextMenu.insertBefore(document.createElement("menuitem"), document.getElementById("context_closeTab"));
  faviconizeTabMenuitem.setAttribute("id", "context_faviconizeTab");
  faviconizeTabMenuitem.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Faviconize Tab" : "\u56FE\u6807\u5316\u6807\u7B7E\u9875");
  faviconizeTabMenuitem.setAttribute("type", "checkbox");
  faviconizeTabMenuitem.setAttribute("accesskey", "I");
  faviconizeTabMenuitem.setAttribute("key", "key_faviconizeTab");
  faviconizeTabMenuitem.setAttribute("oncommand", "gBrowser.faviconizeTab(gBrowser.mContextTab);");
  tabContextMenu.insertBefore(document.createElement("menuseparator"), document.getElementById("context_closeTab"));

  hookCode("gBrowser.updatePopupMenu", "}", function() {
    document.getElementById("context_faviconizeTab").setAttribute("checked", this.mContextTab.hasAttribute("faviconized"));
  });

  hookCode("gBrowser.onTabRestoring", "}", function() {
    var aTab = event.target;
    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (ss.getTabValue(aTab, "faviconized")) {
      aTab.setAttribute("faviconized", true);
      aTab._minWidth = aTab.minWidth;aTab.minWidth = 0;
      aTab._maxWidth = aTab.maxWidth;aTab.maxWidth = 0;
    }
  });

  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
  for (var i=0; i<gBrowser.mTabs.length; i++) {
    var aTab = gBrowser.mTabs[i];
    if (ss.getTabValue(aTab, "faviconized")) {
      aTab.setAttribute("faviconized", true);
      aTab._minWidth = aTab.minWidth;aTab.minWidth = 0;
      aTab._maxWidth = aTab.maxWidth;aTab.maxWidth = 0;
    }
  }

  gBrowser.faviconizeTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (aTab.hasAttribute("faviconized")) {
      aTab.removeAttribute("faviconized");ss.deleteTabValue(aTab, "faviconized");
      aTab.minWidth = aTab._minWidth;
      aTab.maxWidth = aTab._maxWidth;
    }
    else {
      aTab.setAttribute("faviconized", true);ss.setTabValue(aTab, "faviconized", true);
      aTab._minWidth = aTab.minWidth;aTab.minWidth = 0;
      aTab._maxWidth = aTab.maxWidth;aTab.maxWidth = 0;
    }
  }

  gBrowser.mTabContainer.getFirstNonFaviconizedTab = function() {
    for (var i=0; i<this.childNodes.length; i++) {
      if (!this.childNodes[i].hasAttribute("faviconized")) {
        return this.childNodes[i];
      }
    }
    return null;
  }
  hookCode("gBrowser.mTabContainer.adjustTabstrip", "this.firstChild", "(this.getFirstNonFaviconizedTab() || this.firstChild)");
})();

//隐藏标签页
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  var tabContextMenu = document.getAnonymousElementByAttribute(gBrowser, "anonid", "tabContextMenu");
  var hideTabMenuitem = tabContextMenu.insertBefore(document.createElement("menuitem"), document.getElementById("context_closeTab"));
  hideTabMenuitem.setAttribute("id", "context_hideTab");
  hideTabMenuitem.setAttribute("label", getPref("general.useragent.locale").indexOf("zh")==-1 ? "Hide Tab" : "\u9690\u85CF\u6807\u7B7E\u9875");
  hideTabMenuitem.setAttribute("type", "checkbox");
  hideTabMenuitem.setAttribute("accesskey", "H");
  hideTabMenuitem.setAttribute("key", "key_hideTab");
  hideTabMenuitem.setAttribute("oncommand", "gBrowser.hideTab(gBrowser.mContextTab);");

  hookCode("gBrowser.updatePopupMenu", "}", function() {
    document.getElementById("context_hideTab").setAttribute("checked", this.mContextTab.hasAttribute("hidden"));
  });

  hookCode("gBrowser.onTabRestoring", "}", function() {
    var aTab = event.target;
    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (ss.getTabValue(aTab, "hidden")) {
      aTab.setAttribute("hidden", true);
    }
  });

  var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
  for (var i=0; i<gBrowser.mTabs.length; i++) {
    var aTab = gBrowser.mTabs[i];
    if (ss.getTabValue(aTab, "hidden")) {
      aTab.setAttribute("hidden", true);
    }
  }

  gBrowser.hideTab = function(aTab) {
    if (!aTab)
      aTab = this.mCurrentTab;

    var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
    if (aTab.hasAttribute("hidden")) {
      aTab.removeAttribute("hidden");ss.deleteTabValue(aTab, "hidden");
    }
    else {
      aTab.setAttribute("hidden", true);ss.setTabValue(aTab, "hidden", true);
    }
  }
})();

//所有链接在新标签打开
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  hookCode("contentAreaClick", /(?=loadURI)/, function() {
    if (getPref("userChromeJS.openNewTabLite.openAllLinksInTab", false)) {
      openNewTabWith(wrapper.href, wrapper.ownerDocument, null, event, false);
      event.stopPropagation();
      return false;
    }
  });
  hookCode("contentAreaClick", /(?=.*else.*\n.*handleLinkClick)/, function() {
    if (getPref("userChromeJS.openNewTabLite.openAllLinksInTab", false)) {
      openNewTabWith(wrapper.href, wrapper.ownerDocument, null, event, false);
      event.stopPropagation();
      return false;
    }
  });
})();

//在新标签打开链接时继承历史
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  hookCode("openNewTabWith", /(?=.*loadOneTab.*)/, function() {
    if (getPref('userChromeJS.openNewTabLite.openLinkWithHistory', false)) {
      var tab = browser.duplicateTab(browser.selectedTab);
      browser.getBrowserForTab(tab).loadURIWithFlags(aURL, aAllowThirdPartyFixup ? Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP : Ci.nsIWebNavigation.LOAD_FLAGS_NONE, referrerURI, originCharset, aPostData);
      if (!loadInBackground) {
        browser.selectedTab = tab;
      }
      return tab;
    }
  });
  hookCode("openNewWindowWith", /(?=.*openDialog.*)/, function() {
    if (getPref('userChromeJS.openNewTabLite.openLinkWithHistory', false)) {
      var browser = top.document.getElementById("content");
      var tab = browser.duplicateTab(browser.selectedTab);tab.collapsed = true;
      browser.getBrowserForTab(tab).loadURIWithFlags(aURL, aAllowThirdPartyFixup ? Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP : Ci.nsIWebNavigation.LOAD_FLAGS_NONE, referrerURI, charsetArg, aPostData);
      return browser.replaceTabWithWindow(tab);
    }
  });
  hookCode("nsBrowserAccess.prototype.openURI", /.*loadOneTab.*/, function(s) {
    var args = s.split(/var|let|=|\.loadOneTab\(|,|\);/);
    return (function() {
      if (!isExternal && getPref('userChromeJS.openNewTabLite.openLinkWithHistory', false)) {
        var tab = browser.duplicateTab(browser.selectedTab);
        browser.getBrowserForTab(tab).loadURIWithFlags(aURI, aAllowThirdPartyFixup ? Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP : Ci.nsIWebNavigation.LOAD_FLAGS_NONE, aReferrerURI, aCharset, aPostData);
        if (!aLoadInBackground) {
          browser.selectedTab = tab;
        }
      }
      else {
        var tab = browser.loadOneTab(aURI, aReferrerURI, aCharset, aPostData, aLoadInBackground, aAllowThirdPartyFixup);
      }
    }).toString().replace(/^function.*{|}$/g, "")
    .replace(/tab/g, args[1])
    .replace(/browser/g, args[2])
    .replace(/aURI/g, args[3])
    .replace(/aReferrerURI/g, args[4])
    .replace(/aCharset/g, args[5])
    .replace(/aPostData/g, args[6])
    .replace(/aLoadInBackground/g, args[7])
    .replace(/aAllowThirdPartyFixup/g, args[8]);
  });
})();

//单窗口模式
(function() {
  if ("TM_init" in window || "tabberwocky" in window) return;

  if (getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
    if (getPref("browser.link.open_newwindow") == 2)
      setPref("browser.link.open_newwindow", 3);
    if (getPref("browser.link.open_newwindow.restriction") == 1)
      setPref("browser.link.open_newwindow.restriction", 2);
  }

  hookCode("OpenBrowserWindow", "{", function() {
    if (getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
      return BrowserOpenTab();
    }
  });
  hookCode("undoCloseWindow", "{", function() {
    if (getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
      return undoCloseTab.apply(this, arguments);
    }
  });
  hookCode("openNewWindowWith", "{", function() {
    if (getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
      return openNewTabWith.apply(this, arguments);
    }
  });
  hookCode("openUILinkIn", /(?=.*getTopWin.*)/, function() {
    if (where=='window' && getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
      where = 'tab';
    }
  });
  hookCode("nsBrowserAccess.prototype.openURI", /(?=switch \(aWhere\))/, function() {
    if (aWhere==Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW && getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
      aWhere = Ci.nsIBrowserDOMWindow.OPEN_NEWTAB;
    }
  });
  hookCode("gBrowser.replaceTabWithWindow", "{", function() {
    if (getPref('userChromeJS.openNewTabLite.singleWindowMode', false)) {
      return;
    }
  });

  window.addEventListener("popupshown", function(event) {
    var menupopup = event.originalTarget;
    for (var i=0; i<menupopup.childNodes.length; i++) {
      var menuitem = menupopup.childNodes[i];
      switch (menuitem.id) {
        case "menu_newNavigator":
        case "historyUndoWindowMenu":
        case "context-openlink":
        case "context-openframe":
        case "context_openTabInWindow":
        case "placesContext_open:newwindow": {
          menuitem.disabled = getPref('userChromeJS.openNewTabLite.singleWindowMode', false);
        }
      }
    }
  }, false);
})();

function hookCode(orgFunc, orgCode, myCode, orgWindow) {
  if (orgWindow) {
    orgFunc = "orgWindow." + orgFunc;
  }
  if (typeof myCode == "function" && myCode.toString().match(/^function \(\)/)) {
    myCode = myCode.toString().replace(/^function.*{|}$/g, "");
  }
  switch (orgCode) {
    case "{":
      orgCode = /^function.*{/;
      myCode = "$&" + myCode;
      break;
    case "}":
      orgCode = /}$/;
      myCode = myCode + "$&";
      break;
  }
  try {
    eval(orgFunc + "=" + eval(orgFunc).toString().replace(orgCode, myCode));
  }
  catch (e) {
    Cu.reportError("failing to hookCode: " + orgFunc + ", " + e.message);
  }
}

function getPref(aPrefName, aDefault) {
  var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  switch (prefs.getPrefType(aPrefName)) {
    case prefs.PREF_BOOL: return prefs.getBoolPref(aPrefName);
    case prefs.PREF_INT: return prefs.getIntPref(aPrefName);
    case prefs.PREF_STRING: return prefs.getCharPref(aPrefName);
    default:
      switch(typeof aDefault) {
        case "boolean": prefs.setBoolPref(aPrefName, aDefault);break;
        case "number": prefs.setIntPref(aPrefName, aDefault);break;
        case "string": prefs.setCharPref(aPrefName, aDefault);break;
      }
      return aDefault;
  }
}

function setPref(aPrefName, aPrefValue) {
  var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  switch (prefs.getPrefType(aPrefName)) {
    case prefs.PREF_BOOL: prefs.setBoolPref(aPrefName, aPrefValue);break;
    case prefs.PREF_INT: prefs.setIntPref(aPrefName, aPrefValue);break;
    case prefs.PREF_STRING: prefs.setCharPref(aPrefName, aPrefValue);break;
    default:
      switch(typeof aPrefValue) {
        case "boolean": prefs.setBoolPref(aPrefName, aPrefValue);break;
        case "number": prefs.setIntPref(aPrefName, aPrefValue);break;
        case "string": prefs.setCharPref(aPrefName, aPrefValue);break;
      }
  }
}